// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Text;

namespace SnapObjects.Data
{
    public class ModelSqlTokenizer : SqlTokenizerBase
    {
        private readonly IList<string> _tokens;

        public ModelSqlTokenizer(string source)
            : base(source)
        {
            _tokens = this.GetTokens();
        }

        public string GetSql()
        {
            var builder = new StringBuilder();

            foreach (var token in _tokens)
            {
                builder.Append(token);
            }

            return builder.ToString();
        }

        public void ReplaceParameter(string paramName, string replacement)
        {
            //$Param(paramName)
            this.ReplaceFunction("Param", paramName, replacement);

            //:paramName
            for (var i = 0; i < _tokens.Count; i++)
            {
                if (_tokens[i].Equals(":" + paramName, StringComparison.OrdinalIgnoreCase))
                {
                    _tokens[i] = replacement;
                }
            }
        }

        public void ReplaceSubQuery(string sbName, string replacement)
        {
            this.ReplaceFunction("SubQuery", sbName, replacement);
        }

        public void ReplaceProperty(string propertyName, string replacement)
        {
            for (var i = 0; i < _tokens.Count; i++)
            {
                if (_tokens[i].Equals("$") && i + 1 < _tokens.Count &&
                    String.Equals(_tokens[i + 1], propertyName, StringComparison.OrdinalIgnoreCase))
                {
                    _tokens[i] = replacement;
                    this.ResetToken(i + 1, 1);
                }
            }
        }

        public IReadOnlyList<string> GetPropertyRefs()
        {
            var properties = new List<string>();

            for (var i = 0; i < _tokens.Count; i++)
            {
                if (_tokens[i].Equals("$") && i + 1 < _tokens.Count &&
                    (i + 2 == _tokens.Count ||
                        (i + 2 < _tokens.Count &&
                            !String.Equals(_tokens[i + 2], "(", StringComparison.OrdinalIgnoreCase))))
                {
                    properties.Add(_tokens[i + 1]);
                }
            }

            return properties;
        }

        private void ReplaceFunction(string funcType, string funcName, string replacement)
        {
            for (var i = 0; i < _tokens.Count; i++)
            {
                if (_tokens[i].Equals("$") && i + 4 < _tokens.Count &&
                        String.Equals(_tokens[i + 1], funcType, StringComparison.OrdinalIgnoreCase) &&
                        String.Equals(_tokens[i + 2], "(", StringComparison.OrdinalIgnoreCase))
                {
                    if (String.IsNullOrWhiteSpace(_tokens[i + 3]))
                    {
                        if (String.Equals(_tokens[i + 4], funcName, StringComparison.OrdinalIgnoreCase))
                        {
                            if (i + 5 < _tokens.Count &&
                                String.Equals(_tokens[i + 5], ")", StringComparison.OrdinalIgnoreCase))
                            {
                                _tokens[i] = replacement;
                                this.ResetToken(i + 1, 5);
                            }
                            else if (String.IsNullOrWhiteSpace(_tokens[i + 5]) && i + 6 < _tokens.Count &&
                                String.Equals(_tokens[i + 6], ")", StringComparison.OrdinalIgnoreCase))
                            {
                                _tokens[i] = replacement;
                                this.ResetToken(i + 1, 6);
                            }
                        }
                    }
                    else if (String.Equals(_tokens[i + 3], funcName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (i + 4 < _tokens.Count &&
                            String.Equals(_tokens[i + 4], ")", StringComparison.OrdinalIgnoreCase))
                        {
                            _tokens[i] = replacement;
                            this.ResetToken(i + 1, 4);
                        }
                        else if (String.IsNullOrWhiteSpace(_tokens[i + 4]) && i + 5 < _tokens.Count &&
                            String.Equals(_tokens[i + 5], ")", StringComparison.OrdinalIgnoreCase))
                        {
                            _tokens[i] = replacement;
                            this.ResetToken(i + 1, 5);
                        }
                    }
                }
            }
        }

        private void ResetToken(int start, int count)
        {
            for (var i = 0; i < count; i++)
            {
                _tokens[start + i] = String.Empty;
            }
        }
    }
}
